package cn.felord.dmn.config;

import cn.felord.dmn.kogito.SimpleDecisionConfig;
import cn.felord.dmn.kogito.SimpleDecisionModels;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.util.StdDateFormat;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.kie.dmn.api.core.DMNRuntime;
import org.kie.dmn.api.core.event.DMNRuntimeEventListener;
import org.kie.kogito.Addons;
import org.kie.kogito.Application;
import org.kie.kogito.Config;
import org.kie.kogito.KogitoConfig;
import org.kie.kogito.KogitoEngine;
import org.kie.kogito.KogitoGAV;
import org.kie.kogito.StaticApplication;
import org.kie.kogito.StaticConfig;
import org.kie.kogito.config.ConfigBean;
import org.kie.kogito.config.StaticConfigBean;
import org.kie.kogito.decision.DecisionConfig;
import org.kie.kogito.decision.DecisionEventListenerConfig;
import org.kie.kogito.dmn.DMNKogito;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.TimeZone;

import static org.kie.dmn.feel.util.ClassLoaderUtil.CAN_PLATFORM_CLASSLOAD;

/**
 * @author dax
 * @since 2023/12/28
 */
@Configuration(proxyBeanMethods = false)
//@ComponentScan(basePackages = {"org.kie.kogito.**", "org.drools.**", "http**"})
public class KogitoConfiguration {
    @Value("${kogito.service.url:#{null}}")
    private Optional<String> kogitoService;

    @Value("${kogito.messaging.as-cloudevents:#{true}}")
    private boolean useCloudEvents;

//    @Value("${kogito.jackson.fail-on-empty-bean:#{false}}")
//    private boolean failOnEmptyBean;


    @Bean
    ConfigBean configBean() {
        KogitoGAV gav = new KogitoGAV("cn.felord",
                "dmn-kogito-spring-boot-samples",
                "0.0.1-SNAPSHOT");
        return new StaticConfigBean(kogitoService.orElse(""), useCloudEvents, gav);
    }

    @Bean
    Config appAddonsConfig(Collection<KogitoConfig> configs) {
        KogitoConfig[] configsArray = configs.toArray(new KogitoConfig[0]);
        return new StaticConfig(new Addons(Set.of()), configsArray);
    }


    @Bean
    @Scope(value = "application", proxyMode = ScopedProxyMode.TARGET_CLASS)
    SimpleDecisionModels simpleDecisionModels(DecisionConfig decisionConfig,ConfigBean configBean) {
        InputStreamReader inputStreamReader = readResource(Application.class.getResourceAsStream("/User Score.dmn"), "UTF-8");

        DMNRuntime dmnRuntime = DMNKogito.createGenericDMNRuntime(inputStreamReader);

        decisionConfig.decisionEventListeners().listeners().forEach(dmnRuntime::addListener);
        return new SimpleDecisionModels(dmnRuntime,configBean.getGav().orElse(KogitoGAV.EMPTY_GAV));
    }


    @Bean
    @Scope(value = "application", proxyMode = ScopedProxyMode.TARGET_CLASS)
    Application application(Config config, List<KogitoEngine> engines) {
        KogitoEngine[] enginesArray = engines.toArray(new KogitoEngine[0]);
        return new StaticApplication(config, enginesArray);
    }

    @Bean
    DecisionConfig decisionConfig(List<DecisionEventListenerConfig> decisionEventListenerConfigs,
                                  List<DMNRuntimeEventListener> dmnRuntimeEventListeners) {
        return new SimpleDecisionConfig(decisionEventListenerConfigs, dmnRuntimeEventListeners);
    }

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer customizeGlobalObjectMapper(ConfigBean configBean) {
        return new Jackson2ObjectMapperBuilderCustomizer() {

            @Override
            public void customize(Jackson2ObjectMapperBuilder builder) {
                if (!configBean.failOnEmptyBean()) {
                    builder.featuresToDisable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
                }
                builder.dateFormat(new StdDateFormat().withColonInTimeZone(true).withTimeZone(TimeZone.getDefault()));
                builder.modulesToInstall(new JavaTimeModule());
            }
        };
    }

    protected static java.io.InputStreamReader readResource(java.io.InputStream stream, String encoding) {
        if (CAN_PLATFORM_CLASSLOAD) {
            return isrWithEncodingOrFallback(stream, encoding);
        }

        try {
            byte[] bytes = org.drools.util.IoUtils.readBytesFromInputStream(stream);
            java.io.ByteArrayInputStream byteArrayInputStream = new java.io.ByteArrayInputStream(bytes);
            return isrWithEncodingOrFallback(byteArrayInputStream, encoding);
        } catch (java.io.IOException e) {
            throw new java.io.UncheckedIOException(e);
        }
    }

    private static java.io.InputStreamReader isrWithEncodingOrFallback(java.io.InputStream stream, String encoding) {
        try {
            return new java.io.InputStreamReader(stream, encoding);
        } catch (UnsupportedEncodingException e) {

            return new java.io.InputStreamReader(stream);
        }
    }
}
